/*
 *      Interactive disassembler (IDA).
 *      Copyright (c) 1990-99 by Ilfak Guilfanov.
 *      ALL RIGHTS RESERVED.
 *                              E-mail: ig@datarescue.com
 *
 *      Atmel AVR - 8-bit RISC processor
 *
 */

#include "spu.h"
#include "opcode\spu.h"

//--------------------------------------------------------------------------
inline uint32 code_address(signed int delta)
{
  ea_t newip = cmd.ip + 1 + delta;
  int size = romsize ? romsize : 0x10000;
  return newip % size;
}

//--------------------------------------------------------------------------
inline void opreg(op_t &x, uint16 n)
{
  x.type = o_reg;
  x.dtyp = dt_byte;
  x.reg  = n;
}

//--------------------------------------------------------------------------
inline void opimm(op_t &x, int value)
{
  x.type   = o_imm;
  x.dtyp   = dt_byte;
  x.value  = value;
}

//--------------------------------------------------------------------------
inline void opnear(op_t &x, ea_t addr)
{
  x.type   = o_near;
  x.dtyp   = dt_code;
  x.addr   = addr;
}

//--------------------------------------------------------------------------
inline void opmem(op_t &x, ea_t addr)
{
  x.type   = o_mem;
  x.dtyp   = dt_byte;
  x.offb   = 0;
  x.addr   = addr;
}

//--------------------------------------------------------------------------
static void tworegs(int code)
{
  opreg(cmd.Op1, (code >> 4) & 31);
  opreg(cmd.Op2, (code & 15) | ((code >> 5) & 16));
}

//--------------------------------------------------------------------------
inline void opregd(op_t &x, int code)
{
  opreg(x, (code >> 4) & 31);
}

//--------------------------------------------------------------------------
inline void opphr(op_t &x, phrase_t phrase)
{
  x.type   = o_phrase;
  x.phrase = phrase;
  x.dtyp   = dt_byte;
}

//--------------------------------------------------------------------------
inline void opport(op_t &x, int portnum)
{
  x.type   = o_port;
  x.addr   = portnum;
  x.dtyp   = dt_byte;
}

//--------------------------------------------------------------------------
inline void opdisp(op_t &x, phrase_t phrase, int delta)
{
  if ( delta)
  {
    x.type   = o_displ;
    x.phrase = phrase;
    x.addr   = delta;
    x.dtyp   = dt_byte;
  }
  else
  {
    opphr(x, phrase);
  }
}

extern const struct spu_opcode spu_opcodes[];
extern const int spu_num_opcodes;

const struct spu_opcode *spu_disassemble_table[(1<<11)];

static void init_spu_disassemble (void)
{
	int i;

	/* If two instructions have the same opcode then we prefer the first
	* one.  In most cases it is just an alternate mnemonic. */
	for (i = 0; i < spu_num_opcodes; i++)
	{
		int o = spu_opcodes[i].opcode;
		if (o >= (1 << 11))
			abort ();
		if (spu_disassemble_table[o] == 0)
			spu_disassemble_table[o] = &spu_opcodes[i];
		if (Instructions[o].name == 0) {
			Instructions[o].name = spu_opcodes[i].mnemonic;
			for (int j = 1;  j <= spu_opcodes[i].arg[0]; j++) {
				if(i==M_STOP||i==M_STOP2||i==M_STOPD) Instructions[o].feature |= CF_STOP;
				if(i==M_BR) Instructions[o].feature |= CF_JUMP;
				Instructions[o].feature |= spu_opcodes[i].flags;
			}
		}
	}
}

/* Determine the instruction from the 10 least significant bits. */
static const struct spu_opcode *
get_index_for_opcode (unsigned int insn)
{
  const struct spu_opcode *op_index;
  unsigned int opcode = insn >> (32-11);

  /* Init the table.  This assumes that element 0/opcode 0 (currently
   * NOP) is always used */
  if (spu_disassemble_table[0] == 0)
    init_spu_disassemble ();

  if ((op_index = spu_disassemble_table[opcode & 0x780]) != 0
      && op_index->insn_type == RRR)
    return op_index;

  if ((op_index = spu_disassemble_table[opcode & 0x7f0]) != 0
      && (op_index->insn_type == RI18 || op_index->insn_type == LBT))
    return op_index;

  if ((op_index = spu_disassemble_table[opcode & 0x7f8]) != 0
      && op_index->insn_type == RI10) 
    return op_index;

  if ((op_index = spu_disassemble_table[opcode & 0x7fc]) != 0
      && (op_index->insn_type == RI16))
    return op_index;

  if ((op_index = spu_disassemble_table[opcode & 0x7fe]) != 0
      && (op_index->insn_type == RI8))
    return op_index;

  if ((op_index = spu_disassemble_table[opcode & 0x7ff]) != 0)
    return op_index;

  return 0;
}

/* Determine the instruction from the 10 least significant bits. */
const unsigned int
get_idx_for_opcode (unsigned int insn)
{
  const struct spu_opcode *op_index;
  unsigned int opcode = insn >> (32-11);

  /* Init the table.  This assumes that element 0/opcode 0 (currently
   * NOP) is always used */
  if (spu_disassemble_table[0] == 0)
    init_spu_disassemble ();

  if ((op_index = spu_disassemble_table[opcode & 0x780]) != 0
      && op_index->insn_type == RRR)
    return opcode & 0x780;

  if ((op_index = spu_disassemble_table[opcode & 0x7f0]) != 0
      && (op_index->insn_type == RI18 || op_index->insn_type == LBT))
    return opcode & 0x7f0;

  if ((op_index = spu_disassemble_table[opcode & 0x7f8]) != 0
      && op_index->insn_type == RI10) 
    return opcode & 0x7f8;

  if ((op_index = spu_disassemble_table[opcode & 0x7fc]) != 0
      && (op_index->insn_type == RI16))
    return opcode & 0x7fc;

  if ((op_index = spu_disassemble_table[opcode & 0x7fe]) != 0
      && (op_index->insn_type == RI8))
    return opcode & 0x7fe;

  if ((op_index = spu_disassemble_table[opcode & 0x7ff]) != 0)
    return opcode & 0x7ff;

  return 0;
}

//--------------------------------------------------------------------------
int ana(void)
{
	int value;
	int hex_value;
	int insn = ua_next_long();

	const struct spu_opcode *op_index = get_index_for_opcode(insn);
	if(!op_index) { cmd.itype=0; return 0; }

	cmd.itype = get_idx_for_opcode(insn);
	int temp = cmd.itype;

	int fb = (insn >> (32-18)) & 0x7f;
	switch ((enum spu_insns)(&spu_opcodes[cmd.itype] - spu_opcodes))
	{
		case M_HBR:
			if (fb & 0x40) return cmd.size;
			break;
		default:
			break;
	}
		
	hex_value = 0;
	for (int i = 0;  i < op_index->arg[0]; i++)
	{
		switch (op_index->arg[i+1])
		{
			case A_T:
				opreg(cmd.Operands[i], DECODE_INSN_RT (insn));
				break;
			case A_A:
				opreg(cmd.Operands[i], DECODE_INSN_RA (insn));
				break;
			case A_B:
				opreg(cmd.Operands[i], DECODE_INSN_RB (insn));
				break;
			case A_C:
				opreg(cmd.Operands[i], DECODE_INSN_RC (insn));
				break;
			case A_S:
				opreg(cmd.Operands[i], DECODE_INSN_RA (insn));
				break;
			case A_H:
				opreg(cmd.Operands[i], DECODE_INSN_RA (insn));
				break;
			case A_P:
				break;
			case A_U7A:
				opreg(cmd.Operands[i], 173 - DECODE_INSN_U8 (insn));
				break;
			case A_U7B:
				opreg(cmd.Operands[i], 155 - DECODE_INSN_U8 (insn));
				break;
			case A_S3:
			case A_S6:
			case A_S7:
			case A_S7N:
			case A_U3:
			case A_U5:
			case A_U6:
			case A_U7:
				opimm(cmd.Operands[i], DECODE_INSN_I7 (insn));
				break;
			case A_S11:
				opnear(cmd.Operands[i], cmd.ea + DECODE_INSN_I9a (insn) * 4);
				break;
			case A_S11I:
				opnear(cmd.Operands[i], cmd.ea + DECODE_INSN_I9b (insn) * 4);
				break;
			case A_S10:
			case A_S10B:
				opimm(cmd.Operands[i], DECODE_INSN_I10 (insn));
				break;
			case A_S14:
				opimm(cmd.Operands[i], DECODE_INSN_I10 (insn) * 16);
				break;
			case A_S16:
			case A_X16:
				opimm(cmd.Operands[i], DECODE_INSN_I16 (insn));
				break;
			case A_R18:
				opnear(cmd.Operands[i], (cmd.ea + (DECODE_INSN_I16 (insn) * 4)) & 0x3ffff);
				break;
			case A_S18:
				opnear(cmd.Operands[i], DECODE_INSN_U16 (insn) * 4);
				break;
			case A_U18:
				opmem(cmd.Operands[i], DECODE_INSN_U18 (insn));
				break;
			case A_U14:
				opimm(cmd.Operands[i], DECODE_INSN_U14 (insn));
				break;
	  }
  }

  return cmd.size;
}

